/**
在 React 中， `setState`  是一个用于更新组件状态的方法。它是类组件中管理状态的核心方法。以下是关于  `setState`  的详细使用说明和总结：
  1. 基本用法
  在类组件中， `setState`  用于更新组件的状态。它接受一个对象或一个函数作为参数。
  示例：
  jsx
  class MyComponent extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 0,
      };
    }

    increment = () => {
      this.setState({ count: this.state.count + 1 });
    };

    render() {
      return (
        <div>
          <p>Count: {this.state.count}</p>
          <button onClick={this.increment}>Increment</button>
        </div>
      );
    }
  }
  2. 使用函数更新状态
  当新的状态依赖于之前的状态时，建议使用函数形式的  `setState` 。这样可以确保你获取到最新的状态。
  示例：
  jsx
  increment = () => {
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  };
  3. 批量更新
  React 会自动对多个  `setState`  调用进行批量更新，以提高性能。这意味着在同一个事件循环中多次调用  `setState`  可能不会立即更新状态，而是合并为一次更新。
  示例：
  jsx
  handleMultipleUpdates = () => {
    this.setState({ count: this.state.count + 1 });
    this.setState({ count: this.state.count + 1 });
    // 这里的 count 可能只会增加一次，而不是两次
  };
  4. 异步性

  `setState`  是异步的，这意味着你不能依赖于  `setState`  立即更新状态。相反，React 会在适当的时候重新渲染组件。

  示例：
  jsx
  increment = () => {
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count); // 这里可能仍然是旧的状态
  };
  5. 使用回调函数

  `setState`  可以接受一个回调函数作为第二个参数，这个回调函数会在状态更新并重新渲染后执行。

  示例：
  jsx
  this.setState({ count: this.state.count + 1 }, () => {
    console.log('Count updated:', this.state.count);
  });
  6. 注意事项

  - 不可变性：在使用  `setState`  时，务必遵循不可变性原则，不要直接修改  `state`  对象。应始终返回一个新的对象。
  - 合并状态： `setState`  会合并新状态与旧状态，而不是替换整个状态对象。
  - 性能优化：在频繁更新状态的情况下，可以考虑使用  `shouldComponentUpdate`  或  `React.memo`  来优化性能。

  7. 总结

  `setState`  是 React 组件中管理状态的关键方法。了解其异步特性、批量更新机制以及如何使用函数形式更新状态，对于编写高效、可维护的 React 组件至关重要。合理使用  `setState`  可以帮助开发者更好地控制组件的行为和性能。
 */
import React, { Component } from 'react'
import { flushSync } from 'react-dom'

function Hello(props) {
  return <h2>{props.message}</h2>
}

export default class App extends Component {
  constructor(props) {
    super()
    this.state = {
      counter: 0,
      message: "Hello World"
    }
  }

  changeText() {
    // setState 是异步的
    // 1. 传入对象
    this.setState({ message: "Hello React" })

    // 2. 传入函数
    // 好处一: 可以在回调函数中编写新的state的逻辑
    // 好处二: 当前的回调函数会将之前的state和props传递进来
    // this.setState((prevState, props) => {
    //   // 1.编写一些对新的state处理逻辑
    //   // 2.可以获取之前的state和props值
    //   console.log(this.state.message, this.props);
      
    //   return { message: "Hello React" }
    // })

    // 3.setState在React的事件处理中是一个异步调用
    // 如果希望在数据更新之后(数据合并), 获取到对应的结果执行一些逻辑代码
    // 那么可以在setState中传入第二个参数: callback
    this.setState({ message: "Hello React" }, () => {
      console.log("setState回调函数执行", this.state.message);
    })
    console.log("setState执行之后", this.state.message);
  }

  syncChangeText() {
    setTimeout(() => {
      // 在react18之前, setTimeout中setState操作, 是同步操作
      // 在react18之后, setTimeout中setState异步操作(批处理)
      flushSync(() => {
        this.setState({ message: "hello flushSync Sync" })
      })
      console.log(this.state.message)
    }, 0);
  }

  increment() {
    // 1. setState 是异步的, 传对象参数写三次, 只会执行一次
    // this.setState({
    //   counter: this.state.counter + 1
    // })
    // this.setState({
    //   counter: this.state.counter + 1
    // })
    // this.setState({
    //   counter: this.state.counter + 1
    // })

    // 2. setState 是异步的, 传函数参数写三次, 会执行三次
    this.setState((state) => {
      return {
        counter: state.counter + 1
      }
    })
    this.setState((state) => {
      return {
        counter: state.counter + 1
      }
    })
    this.setState((state) => {
      return {
        counter: state.counter + 1
      }
    })
  }
  
  render() {
    const { message, counter } = this.state
    
    return (
      <div>
        <h2>message: {message}</h2>
        <button onClick={e => this.changeText()}>修改文本</button>
        <button onClick={e => this.syncChangeText()}>异步修改文本</button>
        <h2>当前计数: {counter}</h2>
        <button onClick={e => this.increment()}>counter+1</button>
        <Hello message={message}/>
      </div>
    )
  }
}
